/// Text Overlay Application: Main Class (partial CommThread)
/// This class contains the entry point of the application and all the 
/// functions requiered to receive and send data from and to the ShowControl system.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Net.NetworkInformation;
using System.Diagnostics;
using System.Runtime.InteropServices;
using CommLayer;
using Tamir.IPLib;
using Tamir.IPLib.Packets;
using Magnolia;

namespace TextOverlay
{
    public partial class TextOverlay
    {

        private string m_ini_DataIP;
        private int m_ini_DataPort;
        private string m_ini_ControlIP;
        private int m_ini_ControlPort;
        private StringComInt m_CommData;
        private UdpSniffer m_CommSync;

        /// <summary>
        /// Initialize the Synchronization(UDP) and Data(TCP) servers.
        /// This function requieres the presence of the Comm.ini file.
        /// </summary>
        public void InitializeServers()
        {
            if (ReadIniSettings())
            {
                try
                {
                    InitData(m_ini_DataIP, m_ini_DataPort);
                    InitSync(m_ini_ControlIP, m_ini_ControlPort);
                }
                catch
                {
                    Track.LogInfo("ERROR Reading Comm.ini Settings");
                }
            }
            else
            {
                Track.LogInfo("ERROR: Unable to locate Comm.ini file");
            }
        }

        /// <summary>
        /// Initialize the DataServer(TCP)
        /// </summary>
        /// <param name="IPToBind"></param>
        /// <param name="newPort"></param>
        public void InitData(string IPToBind, int newPort)
        {
            m_CommData = new StringComTcpIp();

            int i = 0;
            int selection = -1;
            foreach (NetworkInterface nic in
              NetworkInterface.GetAllNetworkInterfaces())
            {
                foreach (UnicastIPAddressInformation addrInfo in
                    nic.GetIPProperties().UnicastAddresses)
                {
                    if (addrInfo.Address.ToString() == IPToBind)
                    {
                        selection = i;
                        break;
                    }
                }
                i++;
            }
            if (selection == -1)
            {
                Track.LogInfo("TCP: Failed to find IP on NICs");
                return;
            }
            // get the selection
            NetworkInterface adapter = NetworkInterface.GetAllNetworkInterfaces()[selection];
            // set and go 
           m_CommData.IPAddress = adapter.GetPhysicalAddress().ToString();
           m_CommData.Port = newPort;
           m_CommData.InitializeServer(LogTCPActivity, ReceiveData, LogTCPActivity);
        }

        /// <summary>
        /// Initialize the SyncServer(UDP)
        /// </summary>
        /// <param name="IPToBind"></param>
        /// <param name="newPort"></param>
        public void InitSync(string IPToBind, int newPort)
        {

            /* Retrieve the device list */
            PcapDeviceList devices = SharpPcap.GetAllDevices();

            /*If no device exists, print error */
            if (devices.Count < 1)
            {
                Track.LogInfo("No device found on this machine");
                return;
            }

            Track.LogInfo("The following devices are available on this machine:");
            Track.LogInfo("----------------------------------------------------");

            int i = 0;
            int selection = -1;

            /* Scan the list printing every entry */
            foreach (PcapDevice dev in devices)
            {
                /* Description */
                Track.LogInfo(String.Format("{0}) {1}", i, dev.PcapDescription));
                Track.LogInfo(String.Format("IP: {0}", dev.PcapIpAddress));
                LocalHost = dev.PcapIpAddress;
                if (dev.PcapIpAddress == IPToBind)
                {
                    selection = i;
                    break;
                }
                i++;
            }


            if (selection == -1)
            {
                Track.LogInfo("Failed to find UDP IP on NICs: "+IPToBind);
                return;
            }

            PcapDevice device = devices[selection];

            m_CommSync = new UdpSniffer(device);
            m_CommSync.Port = newPort;
            m_CommSync.InitializeClient(LogUDPActivity, ReceiveSync, LogUDPActivity); //todo set proper callbacks

        }


        /// <summary>
        /// Logs all abnormal TCP activity reported by the Comm module
        /// </summary>
        /// <param name="time">The time stamp of the activity</param>
        /// <param name="str">Message from the TCP module</param>
        public void LogTCPActivity(DateTime time, string str)
        {
            Track.LogInfo("TCP Activity: " + str);
            MemoryReport();
        }

        /// <summary>
        /// Logs all abnormal UDP activity reported by the Comm module
        /// </summary>
        /// <param name="time">The time stamp of the activity</param>
        /// <param name="str">Message from the UDP module</param>
        public void LogUDPActivity(DateTime time, string str)
        {
            Track.LogInfo("UDP Activity: " + str);
            MemoryReport();
        }

        /// <summary>
        /// Sends a report message to the ShowControl system
        /// </summary>
        /// <param name="message">a string containing the mesage</param>
        public void ReportToSCS(string message)
        {
            //Console.WriteLine(message);
            try
            {
                if (m_CommData != null)
                {
                    m_CommData.QueueSendString(message);
                }
                else
                    Track.LogInfo("Error: // m_CommData==null");
            }
            catch
            {
                Track.LogInfo("Error Reporting To SCS (m_CommData)");
            }
        }

        /// <summary>
        /// Read Comm settings from Comm.ini in the application folder
        /// </summary>
        /// <returns></returns>
        private bool ReadIniSettings()
        {
            FileInfo config_file = new FileInfo(String.Format("{0}\\Comm.ini", System.Windows.Forms.Application.StartupPath));
            if (config_file.Exists)
            {
                // read configuration values
                // Create an instance of StreamReader to read from a file.
                // The using statement also closes the StreamReader.
                using (StreamReader stream_reader = new StreamReader(config_file.FullName))
                {
                    string line;
                    // Read lines from the file until the end of the file is reached.
                    while ((line = stream_reader.ReadLine()) != null)
                    {
                        // assign keywords from this section
                        if (line.ToUpper().StartsWith("DATAIP=") && line.Length > 7)
                            m_ini_DataIP = line.Substring(7);
                        else if (line.ToUpper().StartsWith("DATAPORT=") && line.Length > 9)
                            m_ini_DataPort = Int32.Parse(line.Substring(9));
                        else if (line.ToUpper().StartsWith("CONTROLIP=") && line.Length > 10)
                            m_ini_ControlIP = line.Substring(10);
                        else if (line.ToUpper().StartsWith("CONTROLPORT=") && line.Length > 12)
                            m_ini_ControlPort = Int32.Parse(line.Substring(12));
                    }
                }
                return true;
            }
            else
            {
                Track.LogInfo("LCS: Could not find .ini: " + config_file.FullName);
                return false;
            }
        }

        private DateTime lastSync;
        private int TimeSampling = 0;
        private TimeSpan intervalSync;
        private int SvrTime;
        private string Stats = "Waiting for Sync...";
        private System.Object SyncLock = new System.Object();
        /// <summary>
        /// CallBack function for the SyncSever. This function receives an xml stream from the SyncServer 
        /// containing the time information for all channels and synchronizes each channel to the frame/time 
        /// indicated in the sync signal.
        /// </summary>
        /// <param name="arriveTime">The signal's arriving time</param>
        /// <param name="xmlMessage">The xml stream containing the sync information</param>
        public void ReceiveSync(DateTime arriveTime, string xmlMessage)
        {
            lock (SyncLock)
            {
                try
                {
                    if (xmlMessage.StartsWith(LocalHost))
                    {
                        int Sample1 = Convert.ToInt32(xmlMessage.Substring(LocalHost.Length + 4));
                        int Sample2 = (arriveTime.Second * 1000) + arriveTime.Millisecond;
                        TimeSampling = Sample2 - Sample1;
                        return;
                    }

                    // Calculate the interval
                    if (xmlMessage.StartsWith("<sync>"))
                    {
                        

                        if (Timeline == null) return;
                        intervalSync = arriveTime - lastSync;
                        lastSync = arriveTime;

                        SAX.Parse(xmlMessage);

                        SAX.SelectNode("RealTime");
                        SvrTime = Expression.TimeToFrame(SAX.GetAttribute("RT"), 30);

                        if (localtest != true)
                        //if(true)
                        {
                            for (int x = 0; x < Timeline.Length; x++)
                            {
                                if (Timeline[x] != null)
                                {
                                    SAX.SelectNode("Ch ID=\"" + Timeline[x].ChannelID.ToString() + "\"");
                                    Timeline[x].SyncTime = arriveTime.AddMilliseconds(TimeSampling);
                                    Timeline[x].SyncFrame = Expression.TimeToFrame(SAX.GetAttribute("TT"), 30);
                                    Timeline[x].RTDiff = SvrTime - Timeline[x].SyncFrame;
                                    Timeline[x].JumpFrom = Expression.TimeToFrame(SAX.GetAttribute("JF"), 30);
                                    Timeline[x].JumpTo = Expression.TimeToFrame(SAX.GetAttribute("JT"), 30);
                                    Timeline[x].UnPause = Expression.TimeToFrame(SAX.GetAttribute("UP"), 30);
                                    Timeline[x].SyncMe = true;
                                    Timeline[x].State = Expression.GetIntValue(SAX.GetAttribute("PS"));
                                }
                            }
                        }
                        if (Timeline[0].ChannelID == 1)
                        {
                            SAX.SelectNode("Cap Ch=\"1\"");
                            CloseCaption1.AppendCharacters(SAX.GetAttribute("CC"), "CC1: ");
                            SAX.SelectNode("Cap Ch=\"2\"");
                            CloseCaption2.AppendCharacters(SAX.GetAttribute("CC"), "CC2: ");
                        }

                        SAX.Nulle();
                    }
                }
                catch (Exception ex)
                {
                    Track.LogInfo("Sync Error: " + ex.Message);
                }
            }
        }

        /// <summary>
        /// This function reports to the ShowControl System the current status of the application
        /// </summary>
        private void ReportStatus()
        {
            try
            {
                System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.BelowNormal;
                System.Threading.Thread.Sleep(1);
                int Raster = 0;
                string TextHolding;
                string isTextHolding;
                while (KillBackThread == false)
                {
                    if (Timeline != null)
                    {
                        Raster = Timeline[0].SyncDiff.Milliseconds;
                        while (Raster > 16)
                            Raster = Raster - 16;
                        TextHolding = HoldingText.ToString();
                        isTextHolding = Convert.ToInt32(isHolding).ToString();
                        TimeSpan dR = DateTime.Now.Subtract(LastFrameRenderedAt);
                        if(dR.TotalSeconds < 4)
                            ReportToSCS("<Status PlayerID=\"" + PlayerID.ToString() + "\" LastTimelineUpdate=\"" + LastUpdateProcessed.ToString() + "\" TimingAtLastSync=\"" + Raster.ToString() + "\" Offset=\"" + Timeline[0].FrameOffset.ToString() + "\" LastHoldPointStatus=\"" + isTextHolding + "\" HoldText=\"" + TextHolding + "\" Warning=\"0\" />");
                        // 20011 = Nudge -1 from Sync, 20012 Nudge +1 from Sync
                        if (Timeline[0].FrameSync < 0)
                            ReportToSCS("<Activity ID=\"20011\" Desc=\"\" />");
                        else if (Timeline[0].FrameSync > 0)
                            ReportToSCS("<Activity ID=\"20012\" Desc=\"\" />");
                    }
                    System.Threading.Thread.Sleep(500);
                }
            }
            catch
            {
                Track.LogInfo("ERROR: Exception Throw in ReportStatus()");
            }
        }

    }
}
